#region All usings
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using senSimulator.NetworkComponents;
using senSimulator.DrawableComponents.TrafficLights;
using System.Threading;

#endregion

namespace senSimulator
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Controller : Microsoft.Xna.Framework.Game
    {
        bool pressed = false;
        #region Variables for main controller
        // Components for basic drawing
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        // Basic view settings
        private int _screenWidth    = 0;
        private int _screenHeight   = 0;
        private float _gameSpeed    = 1.0f;
        private int _spawnDistance  = 250;

        // Basic components: traffic lights, background and the rectangle (for drawing a background)
        private Dictionary<char, CrossRoadDirection> _crossRoadDirections;

        //Networkcomponents
        private NetworkController _networkController;

        // Traffic light directions
        private char[] _possibleDirections = { 'N', 'W', 'S', 'E' };
        private int _numberOfDrawIterations = 0;

        // Keyboard stuff
        private KeyboardState _keyboardState;

        //NETWORK SETTINGS (RV)
        private string _ipAddress;
        private bool _networkMode;
        #endregion


        #region Initialization process
        /// <summary>
        /// Network settings
        /// </summary>
        private void SetNetworkSettings()
        {
            _ipAddress = "141.252.218.41";//141.252.233.155 = Rick
            //NETWORKMODE; True = ReadInt32: False = ReadByte
            _networkMode = true;
        }

        
        /// <summary>
        /// Constructor
        /// </summary>
        public Controller()
        {
            // Initialize the graphics device manager
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            // Deactivate FPS limiter
            // IsFixedTimeStep = false;
            // graphics.SynchronizeWithVerticalRetrace = false;

            // Initialize the missing components
            _crossRoadDirections = new Dictionary<char, CrossRoadDirection>();

            // Set the iterations size
            _numberOfDrawIterations = _possibleDirections.Length * 3;

            // Set screen size
            _screenWidth = 768;
            _screenHeight = 768;
            graphics.IsFullScreen = false;

            // Apply the screen size
            graphics.PreferredBackBufferWidth = _screenWidth;
            graphics.PreferredBackBufferHeight = _screenHeight;
            this.IsMouseVisible = true;

            // Initialize networkController
            this.SetNetworkSettings();
            _networkController = new NetworkController(4321, this._ipAddress, this, _networkMode);
            new Thread(new ThreadStart(_networkController.InitializeNetwork)).Start();
        }


        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }


        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Preload all textures
            Texture2D[] _textures = new Texture2D[42];

            //____________ Preload all traffic lane/light textures ____________
            _textures[0] = Content.Load<Texture2D>("Textures//Lanes//PedestrianLeftLane");
            _textures[1] = Content.Load<Texture2D>("Textures//Lanes//NeutralLane");
            _textures[2] = Content.Load<Texture2D>("Textures//Lanes//ExitLane");
            _textures[3] = Content.Load<Texture2D>("Textures//Lanes//PedestrianMiddleLane");
            _textures[4] = Content.Load<Texture2D>("Textures//Lanes//CarLeftLane");
            _textures[5] = Content.Load<Texture2D>("Textures//Lanes//CarMiddleLane");
            _textures[6] = Content.Load<Texture2D>("Textures//Lanes//CarRightLane");
            _textures[7] = Content.Load<Texture2D>("Textures//Lanes//BusLane");
            _textures[8] = Content.Load<Texture2D>("Textures//Lanes//BicycleLane");
            _textures[9] = Content.Load<Texture2D>("Textures//Lanes//PedestrianRightLane");

            // Traffic Light types
            _textures[10] = Content.Load<Texture2D>("Textures//TrafficLights//PedestrianTrafficLightSide");
            _textures[11] = Content.Load<Texture2D>("Textures//TrafficLights//PedestrianTrafficLightCenter");
            _textures[12] = Content.Load<Texture2D>("Textures//TrafficLights//CarTrafficLight");
            _textures[13] = Content.Load<Texture2D>("Textures//TrafficLights//BusTrafficLight");
            _textures[14] = Content.Load<Texture2D>("Textures//TrafficLights//BicycleTrafficLight");
            _textures[15] = Content.Load<Texture2D>("Textures//TrafficLights//BicycleTrafficLightRight");

            // Traffic light states
            // Pedestrian
            _textures[16] = Content.Load<Texture2D>("Textures//ColorStates//PedestrianSideLightRed");
            _textures[17] = Content.Load<Texture2D>("Textures//ColorStates//PedestrianSideLightGreen");
            _textures[18] = Content.Load<Texture2D>("Textures//ColorStates//PedestrianCenterLightRed");
            _textures[19] = Content.Load<Texture2D>("Textures//ColorStates//PedestrianCenterLightGreen");

            // Car
            _textures[20] = Content.Load<Texture2D>("Textures//ColorStates//CarLightRed");
            _textures[21] = Content.Load<Texture2D>("Textures//ColorStates//CarLightOrange");
            _textures[22] = Content.Load<Texture2D>("Textures//ColorStates//CarLightGreen");

            // Bus
            _textures[23] = Content.Load<Texture2D>("Textures//ColorStates//BusOrange");
            _textures[24] = Content.Load<Texture2D>("Textures//ColorStates//BusRed");
            _textures[25] = Content.Load<Texture2D>("Textures//ColorStates//BusLeft");
            _textures[26] = Content.Load<Texture2D>("Textures//ColorStates//BusRight");
            _textures[27] = Content.Load<Texture2D>("Textures//ColorStates//BusStraight");
            
            // BiCycle
            _textures[28] = Content.Load<Texture2D>("Textures//ColorStates//BicycleTrafficLightRed");
            _textures[29] = Content.Load<Texture2D>("Textures//ColorStates//BicycleTrafficLightGreen");
            _textures[30] = Content.Load<Texture2D>("Textures//ColorStates//BicycleTrafficLightRightRed");
            _textures[31] = Content.Load<Texture2D>("Textures//ColorStates//BicycleTrafficLightRightGreen");

            // Off
            _textures[32] = Content.Load<Texture2D>("Textures//ColorStates//None");

            //____________ Preload all traffic textures ____________
            _textures[33] = Content.Load<Texture2D>("Textures//Traffic//GreenCar");
            _textures[34] = Content.Load<Texture2D>("Textures//Traffic//RedCar");
            _textures[35] = Content.Load<Texture2D>("Textures//Traffic//BlueCar");
            _textures[36] = Content.Load<Texture2D>("Textures//Traffic//Bus");
            _textures[37] = Content.Load<Texture2D>("Textures//Traffic//Bicycle");
            _textures[38] = Content.Load<Texture2D>("Textures//Traffic//Bicycle2");
            _textures[39] = Content.Load<Texture2D>("Textures//Traffic//Pedestrian");
            _textures[40] = Content.Load<Texture2D>("Textures//Traffic//Dwarf");
            _textures[41] = Content.Load<Texture2D>("Textures//Traffic//African");

            // Get all texture sizes
            Vector2[] _textureCenters = FindTextureCenter(_textures);

            // Create all traffic light sets
            for (int i = 0; i < _possibleDirections.Length; i++)
            {
                _crossRoadDirections.Add(_possibleDirections[i], new CrossRoadDirection(_possibleDirections[i], _textures, _textureCenters, _gameSpeed, _spawnDistance, this));
            }

            // Create all traffic light sets
            for (int i = 0; i < _possibleDirections.Length; i++)
            {
                _crossRoadDirections[_possibleDirections[i]].SetOtherCrossRoadDirections(_crossRoadDirections);
            }  
        }


        /// <summary>
        /// Fin the center of the texture
        /// </summary>
        /// <param name="texture">Texture</param>
        /// <returns>Center vector 2 (x,y) of a texture</returns>
        public Vector2[] FindTextureCenter(Texture2D[] textures)
        {
            int arrayLength = textures.Length;
            Vector2[] textureCenterArray = new Vector2[arrayLength];
            for (int i = 0; i < arrayLength; i++)
                textureCenterArray[i] = new Vector2(textures[i].Width / 2, textures[i].Height / 2);

            return textureCenterArray;
        }


        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
        }


        /// <summary>
        /// Check if all cross roads are initialized
        /// </summary>
        /// <returns></returns>
        public bool Initialized()
        {
            if (_crossRoadDirections.Count != 4)
                return false;
            return true;
        }
        #endregion


        #region Basic controller functionality
        /// <summary>
        /// Add traffic to the traffic light lane:
        ///  - This function is called by the network class, if a new traffic item should be created
        ///  - Every traffic light has a lane and a set of sensors
        ///  - The traffic light will give the "go" or "no go" signals to the traffic
        /// </summary>
        /// <param name="type">Type of traffic item</param>
        /// <param name="from">Start</param>
        /// <param name="to">End</param>
        public void AddTrafficToTrafficLightLane(string type, string from, string to)
        {
            // Make the type upper case
            type = type.ToUpper();
            char startDirection = from.ElementAt(0);

            // Add the traffic to the cross road
            string trafficLight = _crossRoadDirections[startDirection].AddTraffic(type, from, to);
            if (trafficLight != "" && trafficLight != null)
                _networkController.sendSensorInfo(trafficLight, 100, to);
        }


        /// <summary>
        /// Change the traffic light state
        /// </summary>
        /// <param name="trafficLightID">The ID of a traffic light</param>
        /// <param name="state">The new state of a traffic light</param>
        public void ChangeTrafficLightState(string trafficLightID, int state)
        {
            char direction = trafficLightID.First();
            char trafficLightNr = trafficLightID.Last();
            _crossRoadDirections[direction].SetTrafficLightState(int.Parse(trafficLightNr.ToString()), state);
        }


        /// <summary>
        /// Send the sensor information to the traffic light controller
        /// </summary>
        /// <param name="trafficLightID">ID of the traffic light</param>
        /// <param name="distanceToTrafficLight">Distance to the traffic light (1 or 100 meter)</param>
        public void SendSensorTriggerToController(string trafficLightID, int distanceToTrafficLight, string direction)
        {
            _networkController.sendSensorInfo(trafficLightID, distanceToTrafficLight, direction);
        }


        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Get the current keyboard state
            _keyboardState = Keyboard.GetState();

            // Exit!
            if (_keyboardState.IsKeyDown(Keys.Escape))
                this.Exit();

            // Traffic light testing keys (GREEN)
            if (_keyboardState.IsKeyDown(Keys.Up))
            {
                ChangeTrafficLightState("N6", 4);
                ChangeTrafficLightState("E6", 4);
                ChangeTrafficLightState("S6", 4);
                ChangeTrafficLightState("W6", 4);
            }

            if (_keyboardState.IsKeyDown(Keys.Right))
            {
                ChangeTrafficLightState("N6", 6);
                ChangeTrafficLightState("E6", 6);
                ChangeTrafficLightState("S6", 6);
                ChangeTrafficLightState("W6", 6);
            }

            if (_keyboardState.IsKeyDown(Keys.Down))
            {
                ChangeTrafficLightState("N6", 5);
                ChangeTrafficLightState("E6", 5);
                ChangeTrafficLightState("S6", 5);
                ChangeTrafficLightState("W6", 5);
            }

            if (_keyboardState.IsKeyDown(Keys.G))
            {
                ChangeTrafficLightState("N1", 1);
                ChangeTrafficLightState("N2", 1);
                ChangeTrafficLightState("N3", 1);
                ChangeTrafficLightState("N4", 1);
                ChangeTrafficLightState("N5", 1);
                ChangeTrafficLightState("N6", 5);
                ChangeTrafficLightState("N7", 1);
                ChangeTrafficLightState("N8", 1);

                ChangeTrafficLightState("E1", 1);
                ChangeTrafficLightState("E2", 1);
                ChangeTrafficLightState("E3", 1);
                ChangeTrafficLightState("E4", 1);
                ChangeTrafficLightState("E5", 1);
                ChangeTrafficLightState("E6", 5);
                ChangeTrafficLightState("E7", 1);
                ChangeTrafficLightState("E8", 1);

                ChangeTrafficLightState("S1", 1);
                ChangeTrafficLightState("S2", 1);
                ChangeTrafficLightState("S3", 1);
                ChangeTrafficLightState("S4", 1);
                ChangeTrafficLightState("S5", 1);
                ChangeTrafficLightState("S6", 5);
                ChangeTrafficLightState("S7", 1);
                ChangeTrafficLightState("S8", 1);

                ChangeTrafficLightState("W1", 1);
                ChangeTrafficLightState("W2", 1);
                ChangeTrafficLightState("W3", 1);
                ChangeTrafficLightState("W4", 1);
                ChangeTrafficLightState("W5", 1);
                ChangeTrafficLightState("W6", 5);
                ChangeTrafficLightState("W7", 1);
                ChangeTrafficLightState("W8", 1);
            }

            if (_keyboardState.IsKeyDown(Keys.O))
            {
                pressed = false;
                ChangeTrafficLightState("N1", 2);
                ChangeTrafficLightState("N2", 2);
                ChangeTrafficLightState("N3", 2);
                ChangeTrafficLightState("N4", 2);
                ChangeTrafficLightState("N5", 2);
                ChangeTrafficLightState("N6", 2);

                ChangeTrafficLightState("E1", 2);
                ChangeTrafficLightState("E2", 2);
                ChangeTrafficLightState("E3", 2);
                ChangeTrafficLightState("E4", 2);
                ChangeTrafficLightState("E5", 2);
                ChangeTrafficLightState("E6", 2);

                ChangeTrafficLightState("S1", 2);
                ChangeTrafficLightState("S2", 2);
                ChangeTrafficLightState("S3", 2);
                ChangeTrafficLightState("S4", 2);
                ChangeTrafficLightState("S5", 2);
                ChangeTrafficLightState("S6", 2);

                ChangeTrafficLightState("W1", 2);
                ChangeTrafficLightState("W2", 2);
                ChangeTrafficLightState("W3", 2);
                ChangeTrafficLightState("W4", 2);
                ChangeTrafficLightState("W5", 2);
                ChangeTrafficLightState("W6", 2);
            }

            if (_keyboardState.IsKeyDown(Keys.R))
            {
                ChangeTrafficLightState("N1", 3);
                ChangeTrafficLightState("N2", 3);
                ChangeTrafficLightState("N3", 3);
                ChangeTrafficLightState("N4", 3);
                ChangeTrafficLightState("N5", 3);
                ChangeTrafficLightState("N6", 3);
                ChangeTrafficLightState("N7", 3);
                ChangeTrafficLightState("N8", 3);

                ChangeTrafficLightState("E1", 3);
                ChangeTrafficLightState("E2", 3);
                ChangeTrafficLightState("E3", 3);
                ChangeTrafficLightState("E4", 3);
                ChangeTrafficLightState("E5", 3);
                ChangeTrafficLightState("E6", 3);
                ChangeTrafficLightState("E7", 3);
                ChangeTrafficLightState("E8", 3);

                ChangeTrafficLightState("S1", 3);
                ChangeTrafficLightState("S2", 3);
                ChangeTrafficLightState("S3", 3);
                ChangeTrafficLightState("S4", 3);
                ChangeTrafficLightState("S5", 3);
                ChangeTrafficLightState("S6", 3);
                ChangeTrafficLightState("S7", 3);
                ChangeTrafficLightState("S8", 3);

                ChangeTrafficLightState("W1", 3);
                ChangeTrafficLightState("W2", 3);
                ChangeTrafficLightState("W3", 3);
                ChangeTrafficLightState("W4", 3);
                ChangeTrafficLightState("W5", 3);
                ChangeTrafficLightState("W6", 3);
                ChangeTrafficLightState("W7", 3);
                ChangeTrafficLightState("W8", 3);
            }

            if (_keyboardState.IsKeyDown(Keys.Q))
            {
                ChangeTrafficLightState("S1", 0);
                ChangeTrafficLightState("S2", 0);
                ChangeTrafficLightState("S3", 0);
                ChangeTrafficLightState("S4", 0);
                ChangeTrafficLightState("S5", 0);
                ChangeTrafficLightState("S6", 0);
                ChangeTrafficLightState("S7", 0);
                ChangeTrafficLightState("S8", 0);
            }

            if (_keyboardState.IsKeyDown(Keys.E))
            {
                ChangeTrafficLightState("S1", 7);
                ChangeTrafficLightState("S2", 7);
                ChangeTrafficLightState("S3", 7);
                ChangeTrafficLightState("S4", 7);
                ChangeTrafficLightState("S5", 7);
                ChangeTrafficLightState("S6", 7);
                ChangeTrafficLightState("S7", 7);
                ChangeTrafficLightState("S8", 7);
            }

            if (_keyboardState.IsKeyDown(Keys.S))
            {
                if (!pressed)
                {
                    pressed = true;
                    AddTrafficToTrafficLightLane("CAR", "N", "W");
                    AddTrafficToTrafficLightLane("CAR", "E", "S");
                    AddTrafficToTrafficLightLane("CAR", "S", "N");
                    AddTrafficToTrafficLightLane("CAR", "W", "E");
                    AddTrafficToTrafficLightLane("BICYCLE", "N", "E");
                    AddTrafficToTrafficLightLane("BICYCLE", "N", "W");
                    AddTrafficToTrafficLightLane("BICYCLE", "E", "N");
                    AddTrafficToTrafficLightLane("BICYCLE", "E", "W");
                    AddTrafficToTrafficLightLane("BICYCLE", "S", "W");
                    AddTrafficToTrafficLightLane("BICYCLE", "S", "E");
                    AddTrafficToTrafficLightLane("BICYCLE", "W", "N");
                    AddTrafficToTrafficLightLane("BICYCLE", "W", "S");

                    AddTrafficToTrafficLightLane("BUS", "E", "W");
                    AddTrafficToTrafficLightLane("BUS", "E", "W");

                    AddTrafficToTrafficLightLane("BUS", "E", "W");
                    AddTrafficToTrafficLightLane("BUS", "E", "W");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "N", "S");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "N", "E");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "W", "N");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "W", "E");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "S", "N");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "S", "W");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "E", "S");
                    AddTrafficToTrafficLightLane("PEDESTRIAN", "E", "W");
                }
            }
            for (int i = 0; i < 4; i++)
            {
                _crossRoadDirections.ElementAt(i).Value.MoveTraffic();
            }
            base.Update(gameTime);
        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            // Prepares graphics device
            spriteBatch.Begin();
            GraphicsDevice.Clear(Color.Black);

            for (int index = 0; index < _numberOfDrawIterations; index++)
            {
                int pointer = index / _possibleDirections.Length;
                switch (pointer)
                {
                    case 0:
                        _crossRoadDirections.ElementAt(index % _possibleDirections.Length).Value.DrawTrafficLanes(gameTime, spriteBatch);
                        break;
                    case 1:
                        _crossRoadDirections.ElementAt(index % _possibleDirections.Length).Value.DrawTraffic(gameTime, spriteBatch);
                        break;
                    case 2:
                        _crossRoadDirections.ElementAt(index % _possibleDirections.Length).Value.DrawTrafficLights(gameTime, spriteBatch);
                        break;
                }
            }

            // Sets the graphics device ready for the next frame!
            spriteBatch.End();

            base.Draw(gameTime);
        }
        #endregion
    }
}